#!/usr/bin/python3
# -- coding: UTF-8 --
__FileName__ = "Parse FIO"
__Author__ = "ShiWei"
__Time__ = "/2024/05/18"
__Version__ = "V2.4"



from openpyxl import Workbook
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter, column_index_from_string
from openpyxl.styles import Font, Alignment, Color, Border, Side, colors, PatternFill
import subprocess
import sys, os, re
import copy


def run_cmd(cmd, err=True):
    """ 执行 shell 命令 """

    status_dict = {
        "status": True,
        "res": None,
    }
    try:
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        stdout, stderr = p.communicate(input=None)
        status_dict["res"] = stdout.strip().decode("utf-8")
        stderr = stderr.strip().decode("utf-8")
        # pcilib: sysfs_read_vpd: read failed: Input/output error
    except Exception as e:
        print(e)
    if stderr:
        if stderr.find("sysfs_read_vpd: read failed:") != -1:
            return status_dict
        if err:
            print(f"报错啦： cmd = {cmd}, ----- err = {stderr}")
        status_dict["status"] = False
    return status_dict


def get_Fio_Log_File_li():
    """获取 Fio 日志文件列表"""

    CMD_LINE = cmd_Find_Fio_File
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        fio_file_li = list(str(jieguo["res"]).split(" "))
        # print(f"""Fio File = {fio_file_li}""")
    if not fio_file_li:
        print("""Sorry! not exists Fio Log File.....""")
        exit()
    else:
        return fio_file_li


def judge_install_openpyxl ():
    """"判断是否安装 openpyxl Python 模块包"""
    CMD_LINE = """pip3 list | egrep 'openpyxl ' | xargs"""
    jieguo = run_cmd(CMD_LINE)
    # print(f"""jieguo["res"] {jieguo["res"]}""")
    if jieguo["status"]:
        openpyxl_res = str(jieguo["res"])
        print(f"""openpyxl_res = {openpyxl_res}""")
        if re.match(r'openpyxl', openpyxl_res):
            print(f"""openpyxl already install:      OK""")
            return
    print(f"""Oh!  Sorry! No module named 'openpyxl' Of python3""")
    exit()



BASEDIR = os.path.dirname(os.path.abspath(__file__))
FILENAME = __file__



cmd_Suffix = " | xargs"
cmd_Find_Iops_Bw_Clat = """awk '/IOPS=|clat.*avg=/{if($0 ~ "IOPS="){ORS="";split($2, iops_li, "=");\
split($4, bw_li, ")");gsub(",", "", iops_li[2]);if(iops_li[2]~/k/){iops_li[2]*=1000};bw=substr(bw_li[1], 2);end=match(bw, "kB/s|MB/s|GB/s", bws);\
bw_num=substr(bw, 1, end - 1);if (bws[0] == "kB/s"){bw_num/=1000;};if (bws[0] == "GB/s"){bw_num*=1000;};\
printf("%-10s   %15s  ", iops_li[2], bw_num)}else{ORS="\\n";cunit=substr($2,2,4);sub(",","",$5);\
sub("avg=","",$5);if (cunit == "msec"){$5*=1000};if (cunit == "nsec"){$5/=1000};\
printf("  %15s ",$5)}}' """
cmd_Find_Device = """awk -F':' '/util=/{print $1}' """
cmd_Find_Mode = """awk -F'rw=' '/rw=/{split($2,li,",");print li[1];}' """
cmd_Find_Ioengine = """awk -F'ioengine=' '/rw=/{split($2,li,",");print li[1];}' """
cmd_Find_Iodepth = """awk -F'iodepth=' '/rw=/{printf("%s ",$2)}' """
cmd_Find_Thread = """awk -F'jobs=' '/jobs=/{split($2, li, ")");printf("%s ",li[1])}' """
cmd_Find_Block = """awk -F'bs=' '/rw=/{split($2,li,"-");split(li[1],li02," ");\
if(li02[2] ~ "KiB"){gsub("KiB","",li02[2]);printf("%s ",int(li02[2]));}else{gsub("B","",li02[2]);\
bs=li02[2]/=1024;printf("%s ",bs);}}' """
cmd_Find_Fio_File = f"""find {BASEDIR}/ -maxdepth 1 -type f | egrep '\.log$' | xargs"""
# 查询系统 软硬件 相关信息
cmd_Query_Bios_Version = """dmidecode  -t bios | grep 'Version:' | sed '1p' -n  | awk '{print $2}' | xargs"""
cmd_Query_Cpu_Mode = """lscpu | awk '/^Model name:/{i=3;while (i<=NF) {print $i; i++} }' | xargs"""
# 查询 盘 相关信息, 注意： 修改盘符
cmd_Disk_Prefix = "smartctl -i /dev/"
cmd_Query_Disk_Fw = """ | awk '/^Firmware Version|^Revision/{print $NF}' | xargs"""
cmd_Query_Disk_Serial = """ | awk '/^Serial Number|^Serial number/{i=3;while (i<=NF) {print $i; i++}}' | xargs"""
cmd_Query_Disk_Rotation_Rate = """ | awk '/^Rotation Rate|^PCI Vendor\/Subsystem ID/{print $0;}' | xargs"""
cmd_Query_Disk_Capacity = """ | awk '/^User Capacity|^Total NVM Capacity/{i=$(NF-1)$NF;gsub("\\\[|\\\]","",i);print i}' | xargs"""
cmd_Query_Disk_Model = """ | awk -F':' '/^Device Model|^Model Number|^Product/{print $2;}' | xargs"""
cmd_Query_Disk_Scheduler = """cat /sys/block/sda/queue/scheduler | sed -e 's#.*\(\[.*\]\).*#\\1#g' -e 's/^.//g' -e 's/.$//g'"""
cmd_Query_Disk_Vendor = """ | awk '/^Device Model|^Model Number|^Vendor/{if($0 ~ /Model/){print $3}else{print $2;}}' | xargs"""

Start_Col_Num = 1
Start_Row_Num = 3
Fio_Mode_Trans_Chinese = {
    "read": "顺序读",
    "write": "顺序写",
    "randread": "随机读",
    "randwrite": "随机写",
    "randrw": "随机混合读写",
}
# 表格标题 所占用的 单元格数
Fio_Title_Cell_Num = 12
Fio_Log_File_li = ""
Fio_Data = {}
Disk_Data = {}



def parse_Fio_File (filename):
    """解析 FIO 日志文件"""

    data_dict = {
        "iops_bw_clats": [],
        "devices": [],
        "modes": [],
        "blocks": [],
        "threads": [],
        "iodepths": [],
        "ioengines": []
    }
    data_len = 0

    parse_Filename = filename

    CMD_LINE = cmd_Find_Iops_Bw_Clat + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        # print(f"""{jieguo["res"]}""")
        iops_bw_clat_lis = list(str(jieguo["res"]).split(" "))
        j, item = 0, []
        iops_bw_clat_li = []
        for i, fff in enumerate(iops_bw_clat_lis):
            if i % 3 == 0 and i != 0:
                iops_bw_clat_li.append(item)
                j += 1
                item = []
            item.append(fff)
        iops_bw_clat_li.append(item)
        # print("iops_bw_clat = ", iops_bw_clat_li)
    if not iops_bw_clat_li:
        print(f"""\033[31mFilename: {parse_Filename} is not valid Fio Log Filename..... \033[0m""")
        return None, None
    data_dict["iops_bw_clats"] = iops_bw_clat_li
    data_len = len(iops_bw_clat_li)

    CMD_LINE = cmd_Find_Device + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        device_li = list(str(jieguo["res"]).split(" "))
        # print(f"""devices = {device_li}""")
        data_dict["devices"] = device_li

    CMD_LINE = cmd_Find_Mode + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        mode_li = list(str(jieguo["res"]).split(" "))
        # print(f"""mode = {mode_li}""")
        data_dict["modes"] = mode_li

    CMD_LINE = cmd_Find_Block + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        block_li = list(str(jieguo["res"]).split(" "))
        # print(f"""block = {block_li}""")
        data_dict["blocks"] = block_li

    CMD_LINE = cmd_Find_Thread + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        thread_li = list(str(jieguo["res"]).split(" "))
        # print(f"""thread = {thread_li}""")
        data_dict["threads"] = thread_li

    CMD_LINE = cmd_Find_Iodepth + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        iodepth_li = list(str(jieguo["res"]).split(" "))
        # print(f"""iodepth = {iodepth_li}""")
        data_dict["iodepths"] = iodepth_li

    CMD_LINE = cmd_Find_Ioengine + parse_Filename + cmd_Suffix
    jieguo = run_cmd(CMD_LINE)
    if jieguo["status"]:
        ioengine_li = list(str(jieguo["res"]).split(" "))
        # print(f"""iodepth = {iodepth_li}""")
        data_dict["ioengines"] = ioengine_li
    # 判断每个键所对应的值的长度是否一致
    if len(data_dict["iops_bw_clats"]) == len(data_dict["devices"]) == len(data_dict["modes"]) \
        == len(data_dict["blocks"]) == len(data_dict["threads"]) == len(data_dict["iodepths"]) \
        == len(data_dict["ioengines"]):
        return data_dict, data_len
    else:
        print(f"""Filename: {parse_Filename} is not valid Fio Log Filename, An error Occurs , Please Check out ..... """)
        return None, None

"""
Fio_Data = {
    "read": {
        "sda": {
            "4k": {
                "2": {
                    "32": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                    "64": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                },
                "8": {
                    "32": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                    "64": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                }
            }
        },
        "sdd": {
            "4k": {
                "2": {
                    "32": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                    "64": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                },
                "8": {
                    "32": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                    "64": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                }
            }
        }
    },
    "randread": {
        "nvme1n1": {
            "4k": {
                "2": {
                    "32": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                    "64": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                },
                "8": {
                    "32": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                    "64": [['1050k', '4302', '242.15'], ['1081k', '4426', '1893.26']]
                }
            }
        }
    }
}
"""

def config_mode(mode_li):
    for mode in set(mode_li):
        if mode not in Fio_Data:
            Fio_Data[mode] = {}


def config_device(device_li, mode_li):
    for ii, device_name in enumerate(device_li):
        if device_name not in Fio_Data[mode_li[ii]]:
            Fio_Data[mode_li[ii]][device_name] = {}


def config_block(fio_data):
    for ii, block in enumerate(fio_data["blocks"]):
        if block not in Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]]:
            Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]][block] = {}


def config_thread (fio_data):
    for ii, thread in enumerate(fio_data["threads"]):
        if thread not in Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]][fio_data["blocks"][ii]]:
            Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]][fio_data["blocks"][ii]][thread] = {}


def config_iodepth (fio_data):
    for ii, iodepth in enumerate(fio_data["iodepths"]):
        if iodepth not in Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]][fio_data["blocks"][ii]][fio_data["threads"][ii]]:
            Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]][fio_data["blocks"][ii]][fio_data["threads"][ii]][iodepth] = []


def config_iops_bw_clat (fio_data):
    for ii, iops_bw_clat in enumerate(fio_data["iops_bw_clats"]):
        Fio_Data[fio_data["modes"][ii]][fio_data["devices"][ii]][fio_data["blocks"][ii]][fio_data["threads"][ii]][fio_data["iodepths"][ii]].append(iops_bw_clat)


def query_disk_protocol (disk_name):
    """ 查询盘的数据读写协议:  SATA,  SAS, NVME"""
    prefix = cmd_Disk_Prefix + disk_name + " | "
    res = None
    cmd_Sata = prefix + "awk '/^SATA Version is/{i=4;while (i<=NF) {print $i; i++}}' | xargs"
    cmd_Sas = prefix + "awk '/^Transport protocol/{i=4;while (i<=NF) {print $i; i++}}' | xargs"
    cmd_Nvme = prefix + "egrep '^PCI Vendor/Subsystem ID'  | xargs"
    cmd_li = [cmd_Sata, cmd_Sas, cmd_Nvme]
    for ii,cmd_line in enumerate(cmd_li):
        jieguo = run_cmd(cmd_line)
        if jieguo["status"]:
            if len(str(jieguo["res"])) != 0:
                if ii == 0:
                    res = "SATA"; break
                elif ii == 1:
                    res = "SAS"; break
                elif ii == 2:
                    res = "NVME"
    return res


def query_disk_medium (disk_name):
    """ 查询盘的存储介质: HDD, SSD
    """
    cmd_line = cmd_Disk_Prefix + disk_name +  cmd_Query_Disk_Rotation_Rate
    jieguo = run_cmd(cmd_line)
    if jieguo["status"]:
        if str(jieguo["res"]).find("PCI") != -1:
            return "NVME"
        elif str(jieguo["res"]).find("Solid") != -1:
            return "SSD"
        elif str(jieguo["res"]).find("rpm") != -1:
            return "HDD"
    return None


def query_disk_info (disk_name, ty):
    """查询盘基本信息
     vendor, fw, serial, model, capacity, scheduler
     """
    cmd_Query_Disk_Scheduler_02 = copy.deepcopy(cmd_Query_Disk_Scheduler)
    cmd_line_dict = {
        "vendor": cmd_Disk_Prefix + disk_name + cmd_Query_Disk_Vendor,
        "fw": cmd_Disk_Prefix + disk_name + cmd_Query_Disk_Fw,
        "serial": cmd_Disk_Prefix + disk_name + cmd_Query_Disk_Serial,
        "model": cmd_Disk_Prefix + disk_name + cmd_Query_Disk_Model,
        "capacity": cmd_Disk_Prefix + disk_name + cmd_Query_Disk_Capacity,
        "scheduler": cmd_Query_Disk_Scheduler_02.replace("sda", disk_name, 1),
    }
    jieguo = run_cmd(cmd_line_dict[ty])
    if jieguo["status"]:
        return str(jieguo["res"])
    return None


def set_up_Disk_Data ():
    """组建所有盘的详细信息大字典
    Disk_Data = {
        "sda": {
            "vendor": "",
            "fw": "",
            ......
        }
        "sdb": {
            "vendor": "",
            "fw": "",
            ......
        }
        "sdc": {
            "vendor": "",
            "fw": "",
            ......
        }
    }
    """
    disk_li = []
    for disks in [[ddd for ddd in dd.keys()] for dd in Fio_Data.values()]:
        disk_li += disks
    for disk in set(disk_li):
        Disk_Data[disk] = {}
        for key in ["vendor", "fw", "serial", "model", "capacity", "scheduler", "medium", "procotol"]:
            try:
                if key == "medium":
                    res = query_disk_medium(disk)
                elif key == "procotol":
                    res = query_disk_protocol(disk)
                else:
                    res = query_disk_info(disk, key)
            except Exception as e:
                res = "NA"
            if not res:
                res = "NA"
            Disk_Data[disk][key] = res


def build_Fio_Json_Data (file_fio_data_dict, data_len = 0):
    """构建 Fio JSON 大数据
    modes, devices, blocks, threads, iodepths, iops_bw_clats
    """
    config_mode(file_fio_data_dict["modes"])
    config_device(file_fio_data_dict["devices"], file_fio_data_dict["modes"])
    config_block(file_fio_data_dict)
    config_thread(file_fio_data_dict)
    config_iodepth(file_fio_data_dict)
    config_iops_bw_clat(file_fio_data_dict)


class create_Excel_Table:
    """
    根据所获取到的 Fio 数据大字典创建 Excel 文件
    """
    def __init__(self, fio_data_dict, start_col, start_row):
        self.book = Workbook()
        self.sheet = self.book.active
        self.fio_data_dict = fio_data_dict
        self.cur_col_index = start_col
        self.cur_row_index = start_row
        self.alignment = Alignment(horizontal='center', vertical='center')
        self.font = Font(name=u'宋体', size=10)
        self.border = Border(left=Side(border_style='thin', color='000000'),
                            right=Side(border_style='thin', color='000000'),
                            top=Side(border_style='thin', color='000000'),
                            bottom=Side(border_style='thin', color='000000'))
        self.fill = PatternFill('solid', fgColor="FFFFFF")


    def get_base_info(self):
        pass

    def get_title_name(self, mode):
        self.mode = mode
        self.title = self.mode

    def set_title_table_cell(self,model_col_index, model_row_index, key):
        """表格标题设置
        厂商 + 媒介(medium) + 协议(procotol) +  容量(capacity) + model + FW
        """
        fir_disk_name = list(Disk_Data.keys())[0]
        title_content = f"""{Disk_Data[fir_disk_name]["vendor"]}_{Disk_Data[fir_disk_name]["medium"]}_{Disk_Data[fir_disk_name]["procotol"]}_{Disk_Data[fir_disk_name]["capacity"]}_{Disk_Data[fir_disk_name]["fw"]}_"""
        title_content = title_content + Fio_Mode_Trans_Chinese[key]
        self.set_cell_content(model_col_index, model_row_index, title_content, font=Font(name=u'宋体', size=13, bold=True), fgColor=Color(rgb='e26b0a'))
        self.sheet.row_dimensions[model_row_index].height = 22
        # 合并 标题 所占用的单元格
        self.set_merge_cells(model_col_index, model_row_index, model_col_index + Fio_Title_Cell_Num, model_row_index)

    def set_merge_cells(self, start_col_num, start_row_num, end_col_num, end_row_num):
        start_col_str = get_column_letter(start_col_num)
        end_col_str = get_column_letter(end_col_num)
        set_str = start_col_str + str(start_row_num) + ":" + end_col_str + str(end_row_num)
        self.sheet.merge_cells(set_str)

    def set_cell_content(self, col_num, row_num, value, alignment=None, font=None, color=None, border=None, fgColor=None):
        col_str = get_column_letter(col_num)
        cell_positin_str = col_str + str(row_num)
        self.sheet[cell_positin_str].value = value
        # 设置字体颜色
        if color:
            self.font.color = color
        # 设置单元格背景色
        if fgColor:
            fill_02 = copy.deepcopy(self.fill)
            fill_02.fgColor = fgColor
            self.sheet[cell_positin_str].fill = fill_02
        # 设置对齐方式
        if not alignment:
            self.sheet[cell_positin_str].alignment = self.alignment
        else:
            self.sheet[cell_positin_str].alignment = alignment
        # 设置边框样式
        if not border:
            self.sheet[cell_positin_str].border = self.border
        else:
            self.sheet[cell_positin_str].border = border
        # 设置字体样式
        if not font:
            self.sheet[cell_positin_str].font = self.font
        else:
            self.sheet[cell_positin_str].font = border


    def run(self):
        """
        Draw Table
        """
        model_col_index, model_row_index = self.cur_col_index, self.cur_row_index
        # 最开始则循环 Fio 模式
        for key, value in self.fio_data_dict.items():
            # 设定标题相关
            self.set_title_table_cell(model_col_index, model_row_index, key)

            self.set_cell_content(model_col_index, model_row_index + 1, "Disk\Type", fgColor=Color(rgb='C4D79B'))
            self.set_merge_cells(model_col_index, model_row_index + 1, model_col_index, model_row_index + 2)
            # mode + capacity
            self.set_cell_content(model_col_index + 1, model_row_index + 1, "Mode", fgColor=Color(rgb='C4D79B'))
            self.set_merge_cells(model_col_index + 1, model_row_index + 1, model_col_index + 1, model_row_index + 2)
            # Medium
            self.set_cell_content(model_col_index + 2, model_row_index + 1, "Medium", fgColor=Color(rgb='C4D79B'))
            self.set_merge_cells(model_col_index + 2, model_row_index + 1, model_col_index + 2, model_row_index + 2)
            # Procotol
            self.set_cell_content(model_col_index + 3, model_row_index + 1, "Procotol", fgColor=Color(rgb='C4D79B'))
            self.set_merge_cells(model_col_index + 3, model_row_index + 1, model_col_index + 3, model_row_index + 2)
            # Serial
            self.set_cell_content(model_col_index + 4, model_row_index + 1, "Serial", fgColor=Color(rgb='C4D79B'))
            self.set_merge_cells(model_col_index + 4, model_row_index + 1, model_col_index + 4, model_row_index + 2)
            # FW
            self.set_cell_content(model_col_index + 5, model_row_index + 1, "Fw", fgColor=Color(rgb='C4D79B'))
            self.set_merge_cells(model_col_index + 5, model_row_index + 1, model_col_index + 5, model_row_index + 2)

            device_col_index = model_col_index
            device_row_index = model_row_index + 3
            devices_num = 1
            # 开始循环遍历该模式下的每一个盘
            for kk02, vv02 in value.items():  # kk02: 盘符
                self.set_cell_content(device_col_index, device_row_index, kk02, fgColor=Color(rgb='538dd5'))
                self.set_cell_content(device_col_index + 1, device_row_index, Disk_Data[kk02]["model"], fgColor=Color(rgb='538DD5'))
                self.set_cell_content(device_col_index + 2, device_row_index, Disk_Data[kk02]["medium"], fgColor=Color(rgb='538DD5'))
                self.set_cell_content(device_col_index + 3, device_row_index, Disk_Data[kk02]["procotol"], fgColor=Color(rgb='538DD5'))
                self.set_cell_content(device_col_index + 4, device_row_index, Disk_Data[kk02]["serial"], fgColor=Color(rgb='538DD5'))
                self.set_cell_content(device_col_index + 5, device_row_index, Disk_Data[kk02]["fw"], fgColor=Color(rgb='538DD5'))
                data_col_index = device_col_index + 6
                data_row_index = device_row_index
                Max_Iops_Num = 0
                Max_Bw_Num = 0
                Min_Clat_Num = 0
                # 开始循环写入该盘的测试数据
                for kk03, vv03 in vv02.items(): # kk03: Block
                    for kk04, vv04 in vv03.items():  # kk04: Thread
                        for kk05, vv05 in vv04.items():   # kk05: Iodepth ; vv05: [['3', '4', '5'],]
                            # 若数据列表为空
                            if len(vv05) == 0:
                                break
                            # 若是第一个设备则打印标题
                            if devices_num == 1:
                                # 设置 Block + Thread + Iodepth 标题
                                cell_content = kk03 + "_" + kk04 + "_" + kk05
                                self.set_cell_content(data_col_index, data_row_index - 2, cell_content, fgColor=Color(rgb='C4D79B'))
                                self.set_merge_cells(data_col_index, data_row_index - 2, data_col_index + 2, data_row_index - 2)
                                # 设置提示符
                                self.set_cell_content(data_col_index, data_row_index - 1, "IOPS", fgColor=Color(rgb='C4D79B'))
                                self.set_cell_content(data_col_index + 1, data_row_index - 1, "BW(MB/s)", fgColor=Color(rgb='C4D79B'))
                                self.set_cell_content(data_col_index + 2, data_row_index - 1, "CLAT(usec)", fgColor=Color(rgb='C4D79B'))
                            # 写入测试数据
                            self.set_cell_content(data_col_index, data_row_index, float(vv05[0][0]), fgColor=Color(rgb='D9D9D9'))
                            self.set_cell_content(data_col_index + 1, data_row_index, float(vv05[0][1]), fgColor=Color(rgb='D9D9D9'))
                            self.set_cell_content(data_col_index + 2, data_row_index, float(vv05[0][2]), fgColor=Color(rgb='D9D9D9'))

                            # Sum_Bw_Num += int(vv05[0][1])
                            # Sum_Clat_Num += int(vv05[0][2])
                            data_col_index += 3

                devices_num += 1
                # 每写完一个盘的数据后，则向下移动一行准备开始写入下一个盘的数据
                device_row_index += 1
                model_row_index += 1
            # 在每张表格最后一行后再加一行， 用于统计每列数据的极值
            disk_num = len(value.keys())
            range_start_row_index = device_row_index - disk_num
            range_end_row_index = device_row_index - 1
            for col_index in range(device_col_index + 6, data_col_index):
                range_start_col_index = get_column_letter(col_index)
                tongji_cell_content = None
                if (col_index - 6 ) % 3 != 0:
                    tongji_cell_content = f"""=MAX({range_start_col_index}{range_start_row_index}:{range_start_col_index}{range_end_row_index})"""
                else:
                    tongji_cell_content = f"""=MIN({range_start_col_index}{range_start_row_index}:{range_start_col_index}{range_end_row_index})"""
                self.set_cell_content(col_index, device_row_index, tongji_cell_content, fgColor=Color(rgb='00B050'))
            self.set_cell_content(device_col_index + 6 - 1, device_row_index, "统计:", fgColor=Color(rgb='00B050'), font=Font(name=u'宋体', size=11, bold=True))

            # 向下移动行， 开始配置写入下一个模式
            model_row_index += 6
        # 保存数据
        self.book.save("disk.xlsx")


    def test(self):
        print(self.fio_data_dict)


if __name__ == "__main__":
    # 判断 Python 解释器版本
    if not sys.version_info >= (3, 6):
        print("python 版本太小了, 要求 >= 3.6.x ")
        exit()

    # 判断是否安装 openpyxl python 模块包
    # judge_install_openpyxl()

    # 开始查找 Fio 日志文件
    for fff in get_Fio_Log_File_li():
        # 判断文件大小
        size = os.path.getsize(fff)
        if size == 0:
            continue
        jieguo_json_data, data_len = parse_Fio_File(fff)
        if jieguo_json_data:
            # 开始组建 大 JSON 数据
            build_Fio_Json_Data(jieguo_json_data, data_len = data_len)
    #print(f"""Fio_Data ========== {Fio_Data}""")
	
    set_up_Disk_Data()
    create_Excel_Table(Fio_Data, Start_Col_Num, Start_Row_Num).run()

